package org.acghub.common;

import java.util.*;

public final class CollectionUtils extends ObjectUtils {

    private static <E> Collection<E> subtract(Collection<E> c1, Collection<E> c2, CollectionCreator<E> collectionCreator) {
        assertNonNull(collectionCreator, "collectionCreator can not be null");
        if (isNull(c1)) {
            return collectionCreator.create();
        }
        if (isNull(c2)) {
            return collectionCreator.create(c1);
        }
        Collection<E> c = collectionCreator.create();
        for (E e : c1) {
            if (!c2.contains(e)) {
                c.add(e);
            }
        }
        return c;
    }

    private static <E> Collection<E> union(Collection<E> c1, Collection<E> c2, CollectionCreator<E> collectionCreator) {
        assertNonNull(collectionCreator, "collectionCreator can not be null");
        if (isNull(c1) && isNull(c2)) {
            return collectionCreator.create();
        }
        Collection<E> c = collectionCreator.create();
        if (isNotNull(c1)) {
            c.addAll(c1);
        }
        if (isNotNull(c2)) {
            c.addAll(c2);
        }
        return c;
    }

    private static <E> Collection<E> intersect(Collection<E> c1, Collection<E> c2, CollectionCreator<E> collectionCreator) {
        assertNonNull(collectionCreator, "collectionCreator can not be null");
        if (isNull(c1) || isNull(c2)) {
            return collectionCreator.create();
        }
        Collection<E> c = collectionCreator.create();
        for (E e : c1) {
            if (c2.contains(e)) {
                c.add(e);
            }
        }
        return c;
    }

    private static <E> Collection<E> disjunction(Collection<E> c1, Collection<E> c2, CollectionCreator<E> collectionCreator) {
        assertNonNull(collectionCreator, "collectionCreator can not be null");
        if (isNull(c1)) {
            return isNull(c2) ? collectionCreator.create() : collectionCreator.create(c2);
        }
        if (isNull(c2)) {
            return collectionCreator.create(c1);
        }
        Collection<E> c = collectionCreator.create();
        for (E e : c1) {
            if (!c2.contains(e)) {
                c.add(e);
            }
        }
        for (E e : c2) {
            if (!c1.contains(e)) {
                c.add(e);
            }
        }
        return c;
    }

    public static <E> List<E> subtractList(Collection<E> c1, Collection<E> c2) {
        return (List<E>) subtract(c1, c2, CollectionCreator.ARRAY_LIST);
    }

    public static <E> Set<E> subtractSet(Collection<E> c1, Collection<E> c2) {
        return (Set<E>) subtract(c1, c2, CollectionCreator.HASH_SET);
    }

    public static <E> NavigableSet<E> subtractTreeSet(Collection<E> c1, Collection<E> c2) {
        return (NavigableSet<E>) subtract(c1, c2, CollectionCreator.TREE_SET);
    }

    public static <E> List<E> unionList(Collection<E> c1, Collection<E> c2) {
        return (List<E>) union(c1, c2, CollectionCreator.ARRAY_LIST);
    }

    public static <E> Set<E> unionSet(Collection<E> c1, Collection<E> c2) {
        return (Set<E>) union(c1, c2, CollectionCreator.HASH_SET);
    }

    public static <E> NavigableSet<E> unionTreeSet(Collection<E> c1, Collection<E> c2) {
        return (NavigableSet<E>) union(c1, c2, CollectionCreator.TREE_SET);
    }

    public static <E> List<E> intersectList(Collection<E> c1, Collection<E> c2) {
        return (List<E>) intersect(c1, c2, CollectionCreator.ARRAY_LIST);
    }

    public static <E> Set<E> intersectSet(Collection<E> c1, Collection<E> c2) {
        return (Set<E>) intersect(c1, c2, CollectionCreator.HASH_SET);
    }

    public static <E> NavigableSet<E> intersectTreeSet(Collection<E> c1, Collection<E> c2) {
        return (NavigableSet<E>) intersect(c1, c2, CollectionCreator.TREE_SET);
    }

    public static <E> List<E> disjunctionList(Collection<E> c1, Collection<E> c2) {
        return (List<E>) disjunction(c1, c2, CollectionCreator.ARRAY_LIST);
    }

    public static <E> Set<E> disjunctionSet(Collection<E> c1, Collection<E> c2) {
        return (Set<E>) disjunction(c1, c2, CollectionCreator.HASH_SET);
    }

    public static <E> NavigableSet<E> disjunctionTreeSet(Collection<E> c1, Collection<E> c2) {
        return (NavigableSet<E>) disjunction(c1, c2, CollectionCreator.TREE_SET);
    }

    interface CollectionCreator<E> {
        CollectionCreator ARRAY_LIST = new CollectionCreator() {
            @Override
            public Collection create() {
                return new ArrayList();
            }

            @Override
            public Collection create(Collection c) {
                return new ArrayList(c);
            }

            @Override
            public Collection create(int size) {
                return new ArrayList(size);
            }
        };
        CollectionCreator HASH_SET = new CollectionCreator() {
            @Override
            public Collection create() {
                return new HashSet();
            }

            @Override
            public Collection create(Collection c) {
                return new HashSet(c);
            }

            @Override
            public Collection create(int size) {
                return new HashSet(size);
            }
        };

        CollectionCreator TREE_SET = new CollectionCreator() {
            @Override
            public Collection create() {
                return new TreeSet();
            }

            @Override
            public Collection create(Collection c) {
                return new TreeSet(c);
            }

            @Override
            public Collection create(int size) {
                return new TreeSet();
            }
        };

        Collection<E> create();

        Collection<E> create(Collection<E> c);

        Collection<E> create(int size);

    }

}
